import 'dart:convert';

import 'package:sl_doclever/sl_doclever.dart';
import 'package:sl_doclever/src/doclever/sl_doc_config.dart';
import 'package:sl_doclever/src/doclever/sl_doc_data.dart';
import 'package:core_extension/core_extension.dart';
import 'package:sl_doclever/src/doclever/sl_node.dart';
import 'package:sl_doclever/src/objc/sl_objc.dart';
import 'package:sl_doclever/src/objc/sl_objc_code.dart';
import 'package:sl_doclever/src/utils/sl_consts.dart';
import 'package:sl_doclever/src/utils/sl_templete.dart';

class SLObjcHttp {
  final List<SLDocData> docData;

  /// 请求合并的管理类-HTTP、Model
  Map reuestMap;

  /// 请求分开的管理类-HTTP、Model
  Map separateMap;

  SLObjcHttp(this.docData) {
    try {
      reuestMap = request_body();
      separateMap = request_body_separate();
    } catch (e, s) {
      print('$e,$s');
    }
  }

  Map request_body() {
    var result = {};

    try {
      var importHList = [];
      var httpH = [];
      var httpM = [];

      httpH.add(SLObjcCode.interface(
        httpFileName(SLDocConfig().root),
        httpFileName(SLDocConfig().root),
        author: SLDocConfig().author,
      ));

      if (SLDocConfig().isSingleTon) {
        httpH.add(SLObjcCode.interface_singleTon());
      }

      docData.forEach((doc) {
        var modeFiles = SLObjc(doc).outFiles;
        result.addAll(modeFiles);

        var retMap = getModelFileFromMap(modeFiles);

        if (retMap != null && retMap.isNotEmpty) {
          importHList.add('#import "$retMap"');
        }
        httpH.add(request_body_interface(doc, modeFiles));

        httpM.add(request_body_impl(doc, modeFiles));
        httpM.add(httpProperty_init(doc, modeFiles));
      });

      httpH.add(SLObjcCode.end());
      httpH.insert(0, importHList.join('\n'));
      httpH.insert(0, SLObjcCode.interface_sys());

      if (SLDocConfig().isSingleTon) {
        httpM.insert(0, SLObjcCode.implementation_singleTon());
      }
      httpM.insert(
          0, SLObjcCode.implementation(httpFileName(SLDocConfig().root)));
      httpM.insert(
          0,
          SLObjcCode.implementation_import(
              httpFileName(SLDocConfig().root) + '.h'));
      httpM.insert(0, SLObjcCode.implementation_include());

      httpM.add(SLObjcCode.end());

      result['${httpFileName(SLDocConfig().root)}.h'] = httpH.join('\n\n');
      result['${httpFileName(SLDocConfig().root)}.m'] = httpM.join('\n\n');
    } catch (e, s) {
      print('$e,$s');
    }
    return result;
  }

  Map request_body_separate() {
    var result = {};

    try {
      docData.forEach((doc) {
        var importHList = [];
        var httpH = [];
        var httpM = [];

        httpH.add(SLObjcCode.interface(
          httpFileName(doc.urlLastName),
          doc.name,
          author: SLDocConfig().author,
        ));
        if (SLDocConfig().isSingleTon) {
          httpH.add(SLObjcCode.interface_singleTon());
        }

        var modeFiles = SLObjc(doc).outFiles;
        result.addAll(modeFiles);

        var retMap = getModelFileFromMap(modeFiles);
        if (retMap.isNotEmpty) importHList.add('#import "$retMap"');
        httpH.add(request_body_interface(doc, modeFiles));

        httpM.add(request_body_impl(doc, modeFiles));
        httpM.add(httpProperty_init(doc, modeFiles));

        httpH.add(SLObjcCode.end());
        httpH.insert(0, importHList.join('\n'));
        httpH.insert(0, SLObjcCode.interface_sys());

        if (SLDocConfig().isSingleTon) {
          httpM.insert(0, SLObjcCode.implementation_singleTon());
        }
        httpM.insert(
            0, SLObjcCode.implementation(httpFileName(doc.urlLastName)));
        httpM.insert(
            0,
            SLObjcCode.implementation_import(
                httpFileName(doc.urlLastName) + '.h'));
        httpM.insert(0, SLObjcCode.implementation_include());

        httpM.add(SLObjcCode.end());

        result['${httpFileName(doc.urlLastName)}.h'] = httpH.join('\n\n');
        result['${httpFileName(doc.urlLastName)}.m'] = httpM.join('\n\n');
      });
    } catch (e, s) {
      print('$e,$s');
    }
    return result;
  }

  /// ```
  /// /// 选择小区
  /// /// @discussion /uhomecp-sso/v1/userAppCrLand/queryCommunityListV2
  /// /// @param params {"communityName":"","code":""}
  /// -(void)requestQueryCommunityListV2WithParams:(NSDictionary*)params finished:(void(^)(BOOL isSuccessed,id result,NSString* codeType,NSString* errorMessage))finished
  /// /// 选择小区
  /// @property (nonatomic, strong) SEGQueryCommunityListV2UserInfoModel *queryCommunityListV2;
  /// ```
  String request_body_interface(SLDocData doc, Map modeFiles) {
    var ret = [];

    /// HTTPBLOCK
    var block = HTTPBLOCK;
    if (doc.method == SLHTTPMethod.POST) {
      block = request_method(doc.rawJSON, doc.rawJSONJson, doc);
    } else {
      block = request_method(doc.queryParam, doc.queryParamJson, doc);
    }

    ret.add(block + ';');
    ret.add(httpProperty(doc, modeFiles));

    return ret.join('\n');
  }

  String request_body_impl(SLDocData doc, Map modeFiles) {
    var httpBody = [];

    var input = doc.method == SLHTTPMethod.POST ? doc.rawJSON : doc.queryParam;
    var inputJSON =
        doc.method == SLHTTPMethod.POST ? doc.rawJSONJson : doc.queryParamJson;

    httpBody.add(request_method(input, inputJSON, doc) + '{');
    httpBody.add(request_url(doc.url));

    var para = request_para(input);
    if (para.isNotEmpty) httpBody.add(para);

    httpBody.add(request_method_body(input, doc, modeFiles) + '\n}');

    return httpBody.join('\n\n');
  }

  /// ```
  /// NSString *urlString = [SEGServerManager serverUrlJoint:kClientRootURL,@"/enterprise-app/user/queryUserCondition",nil];
  /// ```
  String request_url(String url) => 'NSString *urlString = @"$url";';

  /// ```
  /// NSMutableDictionary *params = [NSMutableDictionary dictionary];
  /// [params setValue:communityName forKey:@"communityName"];
  /// [params setValue:code forKey:@"code"];
  /// ```
  String request_para(List<SLNode> nodes) {
    var ret = '';

    if (nodes == null || nodes.isEmpty) return ret;
    if (SLDocConfig().isMap) return ret;

    var paramsList = [];
    paramsList
        .add('NSMutableDictionary *params = [NSMutableDictionary dictionary];');
    nodes.forEach((node) {
      paramsList.add('[params setValue:${node.name} forKey:@"${node.name}"];');
    });

    return paramsList.join('\n');
  }

  /// ```
  /// [SEGHTTPBusinessManager seg_requestJSONWithMethod:GET URLString:urlString parameters:params completed:^(BOOL isSuccessed, id result, NSString *codeType, NSError *error) {
  ///
  /// if (!isSuccessed || result == nil) {
  ///     finished(NO,nil,codeType,error.localizedDescription);
  ///     return;
  /// }
  ///
  /// if (![result isKindOfClass:[NSDictionary class]]) {
  ///     finished(NO,nil,codeType,error.localizedDescription);
  ///     return;
  /// }
  ///
  /// self.queryCommunityListV2 = [SEGQueryCommunityListV2UserInfoModel yy_modelWithDictionary:result];
  /// finished(YES,result,codeType,error.localizedDescription);
  /// return;
  /// }];
  /// ```
  String request_method_body(List<SLNode> nodes, SLDocData doc, Map modeFiles) {
    var httpStr = '[SEGHTTPBusinessManager ';

    var isJSONREQ = false;
    if (doc.header != null && doc.header.isNotEmpty) {
      doc.header.forEach((node) {
        if (node.name == 'Content-Type' && node.value == 'application/json') {
          isJSONREQ = true;
          return;
        }
      });
    }

    httpStr +=
        isJSONREQ ? 'seg_requestJSONWithMethod:' : 'seg_requestWithMethod:';

    httpStr += '${doc.method} URLString:urlString parameters:';
    httpStr += (nodes == null || nodes.isEmpty) ? 'nil ' : 'params ';

    httpStr += SLObjcCode.implementation_http(
        getModelNoFormat(getModelFileFromMap(modeFiles)), doc.urlLastName);
    return httpStr;
  }

  /// ```
  /// /// 选择小区
  /// /// @discussion /uhomecp-sso/v1/userAppCrLand/queryCommunityListV2
  /// /// @param params {"communityName":"","code":""}
  /// -(void)requestQueryCommunityListV2WithCommunityName:(NSString*)communityName code:(NSString*)code finished:(void(^)(BOOL isSuccessed,id result,NSString* codeType,NSString* errorMessage))finished
  /// ```
  String request_method(
    List<SLNode> nodes,
    Map nodesMap,
    SLDocData doc, {
    bool isInvoke = false,
  }) {
    var remark = '/// ${doc.name}\n'
        '/// @discussion ${doc.url}\n';

    var vid = isInvoke ? '' : '-(void)';
    var methodStr = '${vid}request${doc.urlLastName.sl_capitalize()}With';

    if (nodes == null || nodes.isEmpty) {
      if (isInvoke) return methodStr += HTTPBLOCK_RAC.sl_capitalize();
      methodStr += HTTPBLOCK.sl_capitalize();
      return remark + methodStr;
    }

    var fNodes = doc.allNodesName(nodes);
    if (SLDocConfig().isMap || (fNodes != null && fNodes.length > 1)) {
      if (isInvoke) return methodStr += 'Params:@{} $HTTPBLOCK_RAC';

      methodStr += 'Params:(NSDictionary*)params $HTTPBLOCK';

      if (nodesMap != null && nodesMap.isNotEmpty) {
        remark += '/// @param params ${json.encode(nodesMap)}\n';
      } else {
        remark += '/// @param params\n';
      }

      return remark + methodStr;
    }

    var parL = [];
    for (var i = 0; i < nodes.length; i++) {
      var el = nodes.elementAt(i).name;
      var elName = i == 0 ? el.sl_capitalize() : el;

      if (isInvoke) {
        parL.add('$elName:@"" ');
      } else {
        parL.add('$elName:(NSString*)$el ');
      }

      remark += '/// @param $el ${nodes.elementAt(i).remark}\n';
    }
    methodStr += parL.join('');

    if (isInvoke) return '$methodStr$HTTPBLOCK_RAC';

    return '$remark$methodStr$HTTPBLOCK';
  }

  /// `SEGQueryCommunityListV2Manager`
  String httpFileName(String urlLastName) =>
      SLDocConfig().prefix +
      urlLastName.sl_capitalize() +
      SLDocConfig().httpEnd;

  /// `queryCommunityListV2Command`
  String httpRACName(String urlLastName) => urlLastName + 'Command';

  /// `queryCommunityListV2Manager`
  String httpObjcName(String urlLastName) =>
      urlLastName + SLDocConfig().httpEnd;

  /// ```
  /// /// 选择小区
  /// @property (nonatomic, strong) SEGQueryCommunityListV2UserInfoModel *queryCommunityListV2;
  /// ```
  String httpProperty(SLDocData doc, Map modeFiles) {
    if (getModelFileFromMap(modeFiles).isNotEmpty) {
      return '/// ${doc.name}\n'
          '@property (nonatomic, strong) ${getModelNoFormat(getModelFileFromMap(modeFiles))} *${doc.urlLastName};';
    }
    return '';
  }

  String httpProperty_init(SLDocData doc, Map modeFiles) =>
      SLObjcCode.implementation_property(
          getModelNoFormat(getModelFileFromMap(modeFiles)), doc.urlLastName);

  /// `Map`  ->  `SEGQueryCommunityListV2UserInfoModel.h`
  String getModelFileFromMap(Map map) {
    if (map == null || map.isEmpty) return '';

    return map.keys
        .takeWhile((value) => (value as String).contains('.h'))
        .first;
  }

  /// `SEGQueryCommunityListV2UserInfoModel.h` -> `SEGQueryCommunityListV2UserInfoModel`
  String getModelNoFormat(String file) {
    return file.replaceAll('.h', '');
  }

  RACInfo rac_all_request() {
    var reqL = <RACInfoDetail>[];

    docData.forEach((doc) {
      /// HTTPBLOCK
      var block = '';
      if (doc.method == SLHTTPMethod.POST) {
        block = request_method(
          doc.rawJSON,
          doc.rawJSONJson,
          doc,
          isInvoke: true,
        );
      } else {
        block = request_method(
          doc.queryParam,
          doc.queryParamJson,
          doc,
          isInvoke: true,
        );
      }
      reqL.add(RACInfoDetail(
        cmd: httpRACName(doc.urlLastName),
        desc: '${doc.name}-RAC',
        mtd: block,
        subNext: SL_DOC_RAC_SUB.replaceAll(
            '[DOCLEVER_OBJC]', httpRACName(doc.urlLastName)),
      ));
    });

    return RACInfo(
      singleTone: '[${httpFileName(SLDocConfig().root)} sharedManager]',
      cls: httpFileName(SLDocConfig().root),
      name: httpObjcName(SLDocConfig().root),
      mtdL: reqL,
    );
  }
}

class RACInfo {
  final String cls;
  final String singleTone;
  final String name;
  final List<RACInfoDetail> mtdL;
  RACInfo({this.singleTone, this.cls, this.name, this.mtdL});
}

class RACInfoDetail {
  final String cmd; // SEGUserInfoManager
  final String mtd;
  final String desc;
  final String subNext;

  RACInfoDetail({this.subNext, this.cmd, this.mtd, this.desc});
}
